Skip to content

Conversation

spalladino
Copy link
Contributor

Related to BLS keys for optimisitc attestation signature verification (see #69)

- The cost of computing an aggregate key and verifying an aggregate signature should not be too expensive in the EVM (no more than 300k gas ideally, as this gets run once every epoch for block proposals). This implies that each individual public key should be small as well, since it will have to be read from storage.
- At the moment, we do not require verifying these signatures on a rollup circuit, but this _may_ change in the future, so it should not be prohibitively expensive.

We propose that a BLS aggregate signature over the [BLS12-381](https://eth2book.info/latest/part2/building_blocks/bls12-381/) curve fits the bill. Public keys are 48 bytes long, aggregated signatures are 96 bytes long, and L1 verification should be doable in under the target gas (@LHerskind [estimated it](https://colab.research.google.com/drive/1gXbNXLuQZw_1n7PhkRVlTGFl69iIjkNE?authuser=1#scrollTo=7h8tC7BEUr5D) at ~200k gas).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the research doc is using keys defined over the bn254 curve, with those precompile costs - costs differ for bls12-381

The requirements for the signing scheme and curve are:

- An aggregated signature should be small (no more than 512 bits ideally) for a 48-validator committee.
- The cost of computing an aggregate key and verifying an aggregate signature should not be too expensive in the EVM (no more than 300k gas ideally, as this gets run once every epoch for block proposals). This implies that each individual public key should be small as well, since it will have to be read from storage.
Copy link
Member

@Maddiaa0 Maddiaa0 Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: they should only need to be read when computing the committee commitment, this reads like they need to be read at verification time

1. Require a proof of possession, validated along with the key during registration
2. Use a modified aggregate public key

Given the second option has not been implemented (at least based on the article linked above), and that it may involve higher gas costs for generating the aggregate public key (which happens on every epoch) as opposed to the one-time registration cost, the first option seems the best.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computing the modified key should be straight forward by reading the committee commitment and using it as the factor for the aggregate key.
it would be 2 scalar mul opcodes


### How do we generate the BLS private key for validators?

We could derive the BLS private key from their ECDSA private key, potentially from having them sign a given message so we do not need direct access to the ECDSA private key. This means that validators do not need to store two separate private keys.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deterministic ECDSA should be a consideration here. It has caused problems in the past, where lattice plus did not implement deterministic nonces

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if using the same private key it would need to be reduced to the field size of BLS12-381


We propose that a BLS aggregate signature over the [BLS12-381](https://eth2book.info/latest/part2/building_blocks/bls12-381/) curve fits the bill. Public keys are 48 bytes long, aggregated signatures are 96 bytes long, and L1 verification should be doable in under the target gas:

> We need to send as CALLDATA the 48 pubkeys for the committee members plus a bitmap of signers, so we can reconstruct both the committee hash and the aggregated pubkey, which is roughly 48 words (`1536` bytes, `61k` gas at 40 gas per slot, or `24k` at 16 gas per slot). The verification itself requires two `SLOAD`s (`4200`), a hash-to-curve (`20k` gas), 33 ECADDs (`5k`), and two pairings (`124k` gas). Total is about `214k` gas, which gets paid once per epoch.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> We need to send as CALLDATA the 48 pubkeys for the committee members plus a bitmap of signers, so we can reconstruct both the committee hash and the aggregated pubkey, which is roughly 48 words (`1536` bytes, `61k` gas at 40 gas per slot, or `24k` at 16 gas per slot). The verification itself requires two `SLOAD`s (`4200`), a hash-to-curve (`20k` gas), 33 ECADDs (`5k`), and two pairings (`124k` gas). Total is about `214k` gas, which gets paid once per epoch.
> We need to send as CALLDATA the 48 pubkeys for the committee members plus a bitmap of signers, so we can reconstruct both the committee hash and the aggregated pubkey, which is roughly 48 words (`1536` bytes, `61k` gas at 40 gas per word, or `24k` at 16 gas per word). The verification itself requires two `SLOAD`s (`4200`), a hash-to-curve (`20k` gas), 33 ECADDs (`5k`), and two pairings (`124k` gas). Total is about `214k` gas, which gets paid once per epoch.


On the other hand, if we know that all entries to GSE have to go through the staking queue, we could instead validate early in the `enqueue`, which would also allow us to change curves in the future without having to modify the GSE.

To favor failing fast, I prefer validating in the queue, assuming we are certain that the queue is always present.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make this more generic- the GSE just trusts that the instances have done whatever validation is necessary, it doesn't need to use a queue per se.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though this gets really risky and weird if/when someone who enters the GSE under instance A, which performs its own validation, but is validating for the "canonical" instance. When the new canonical is added, those validators will be moved along to the new canonical instance B. So, similar to our present assumption that the two instances have the same required deposit amount (as it is stipulated by the GSE), I think we would need to put the validation in the GSE, otherwise we would have no idea whether the things deposited from instance A are still valid for instance B.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's something that wasn't clear to me: the staking queue, as it stands today, depends on the current rollup instance, right? So when someone stakes in the GSE do they always go through a given instance? Does this instance need to be "registered" in the GSE? What are the permissions required for registering an instance?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone stakes in the GSE do they always go through a given instance
Yes, exactly

Does this instance need to be "registered" in the GSE
Yes, exactly

It happens as part of an upgrade. See l1-contracts/test/governance/scenario/RegisterNewRollupVersionPayload.sol

The new instance is added to the registry, and to the GSE.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mitch say, the validation should be in the GSE to ensure that it is valid at the next level as well. Separate, I believe that we need to also ensure that the keys are unique to avoid having duplicates as it makes it pretty confusing who signed, probably also some bigger math issues that my dumb brain cannot understand right now.

Might be possible to "just" have the GSE validate that it is a valid point on curve, if the approaches can be deemed to never require proof of possesion.


We propose that a BLS aggregate signature over the [BLS12-381](https://eth2book.info/latest/part2/building_blocks/bls12-381/) curve fits the bill. Public keys are 48 bytes long, aggregated signatures are 96 bytes long, and L1 verification should be doable in under the target gas:

> We need to send as CALLDATA the 48 pubkeys for the committee members plus a bitmap of signers, so we can reconstruct both the committee hash and the aggregated pubkey, which is roughly 48 words (`1536` bytes, `61k` gas at 40 gas per slot, or `24k` at 16 gas per slot). The verification itself requires two `SLOAD`s (`4200`), a hash-to-curve (`20k` gas), 33 ECADDs (`5k`), and two pairings (`124k` gas). Total is about `214k` gas, which gets paid once per epoch.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things that come to mind:

  1. Don't pick the proposer from the committee (that way we can just compute index without needing knowledge of committee, means that we don't need to "open" the commitment to check proposer.
  2. Use the total aggregate key as the committee commitment, that way you don't need to publish them all, but can only the ones missing, as you can then subtract the missing from the total aggregate key to get the "key of the attesters".

That should allow us to do only 15 ECADDs and publish less data as well.

I'm not fully following the 48 Words here, if there are 48 members and you have each public key that is 48 bytes that require more than 48 32-byte words.

Point 1, is addressed at the rollup level, but I think it is fine to point out early here as it would be accessing a single index on the GSE.


On the other hand, if we know that all entries to GSE have to go through the staking queue, we could instead validate early in the `enqueue`, which would also allow us to change curves in the future without having to modify the GSE.

To favor failing fast, I prefer validating in the queue, assuming we are certain that the queue is always present.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mitch say, the validation should be in the GSE to ensure that it is valid at the next level as well. Separate, I believe that we need to also ensure that the keys are unique to avoid having duplicates as it makes it pretty confusing who signed, probably also some bigger math issues that my dumb brain cannot understand right now.

Might be possible to "just" have the GSE validate that it is a valid point on curve, if the approaches can be deemed to never require proof of possesion.

- `GSE.getAttestersAtTime` -> `GSE.getAttestersBLSPubKeysAtTime`
- `GSE.getAttesterFromIndexAtTime` -> `GSE.getAttesterBLSPubKeyFromIndexAtTime`

## Security considerations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would also expect that there can be some somewhat weird quirks if we are doing simple key aggregation with an attacker registering a and -a so when both are in the committee it effectively reduces the size. Essentially it gives a "fake" safety, as while there might be funds at stake for those, anyone can use it, so it is not bringing much safety. Generally some weird quicks there, so the 2'nd approach mentioned might be a must. Need someone with better math skills than me to look.


If we do not default to generating the BLS private key from the validator ECDSA private key, should we allow validators to change them? It's unclear if rotating should involve a forced delay, so that a validator cannot change their BLS key halfway through an epoch.

To avoid additional complexity, I suggest not to. Worst case, a validator can exit and re-enter with different BLS keys.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the usggestion. Would see it is a similar to rotating the attestation key where we also deem it acceptable to exit. Seeing the bls as attestation key.


### Should we support changing key family in the future?

If we find a better signing algorithm or curve in the future, we have no easy way for validators to switch key scheme. This would require each validator to have both set of keys (the old and new ones) registered simultaneously, so when the canonical rollup instance is changed, the new one can immediately access the new keys.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say DONT SUPPORT THIS. This is something where I would say it is a new GSE that is needed. If the structure can be different between which rollup you registered at, any of the "automatic moving" seems like it could go straight to hell if a new version is expecting a different type because people that are automatically moved might not follow similar constraints to the format or data.

Essentially, a consumer of the values in the GSE should be able to assume that the data in it follow some specific format and have passed certain checks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants